<?php

namespace app\sosi\common;

use Facebook\WebDriver\Interactions\WebDriverActions;
use GuzzleHttp\Client;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverSelect;
use InvalidArgumentException;
use RuntimeException;
use think\facade\Cache;
use think\facade\Log;

class MyWebDriver
{
    use Docker;
    protected $driver = null;
    protected $windows = [];
    protected $host = 'http://localhost:4443/wd/hub';
    protected $redis;
    protected $cookies = [];

    public function __construct()
    {
        $this->redis = Redis::getInstance();
    }

    private static $instance;
    public static function getInstance()
    {
        if (static::$instance === null) {
            static::$instance = new static();
        }
        return static::$instance;
    }
    public function data($params = [])
    {
        return $params;
    }
    public  function toRun($params)
    {
        sleep(1);

        try {
            $options = $this->data($params);
            foreach ($options as $option) {

                $this->runSetp($option);
            }
        } catch (\Throwable $th) {
            $error_type_ = Cache::get('error_type_' . md5($message));
            $error_type_ = $error_type_ ?: 0;
            $error_type_ = $error_type_ + 1;
            Cache::set('error_type_' . md5($message), $error_type_);
            $this->log('操作错误：' . $th->getMessage());
            $this->log('操作错误：' . $th->getTraceAsString());
            $this->refreshDriver();
        }
    }

    public function  fastrun($option)
    {
        if (isset($option['fastrun']) && $option['fastrun'] == true) {
            $key = md5(json_encode([time(), $option['do']]));
            $fastrun = Cache::get($key);

            if ($fastrun == 'oldrun') {
                return true;
            } else {
                Cache::set($key, 'oldrun');
                return false;
            }
        } else {
            return false;
        }
    }
    public  function runSetp($option)
    {
        if ($option) {
            $this->log('-------------------------------------------');
            $this->log('时间：' . date('Y-m-d H:i:s', time()));

            $this->log('操作数据：' . json_encode($option, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

            $type = $option['type'];
            if (method_exists($this, $type)) {
                $this->$type($option['do']);

                if (isset($option['sleep'])) {
                    sleep($option['sleep']);
                }
            } else {
                $this->log('操作类型不存在：' . $option['type']);
            }
        }
    }
    protected function WebDriver()
    {
        $this->log('判断WebDriver');
        if ($this->driver === null) {
            $this->log('WebDriver不存在，获取$sessionId');
            $sessionId = Cache::get('$sessionId');
            if ($sessionId) {
                try {
                    $this->log('$sessionIdc存在，使用$sessionId创建会话：' . $sessionId);
                    $this->driver = RemoteWebDriver::createBySessionID($sessionId, $this->host, 300, 300, true, $this->config());

                    if ($this->driver->getCurrentURL() === 'about:blank') {
                        Cache::set('$sessionId', false);
                        $this->driver->quit();
                        $this->driver = $this->createNewWebDriver();
                    }
                    $this->driver->manage()->window()->maximize();
                    $this->log('恢复会话完成');
                } catch (\Exception $e) {
                    Cache::set('$sessionId', false);
                    $this->log('恢复会话失败,删除$sessionId，重新连接');
                    $this->WebDriver();
                }
            } else {
                $this->log('$sessionIdc不存在，创建会话：');
                Cache::set('$sessionId', false);
                $this->driver = $this->createNewWebDriver();
            }
        }


        return $this->driver;
    }

    /**
     * 创建Driver
     * @return RemoteWebDriver
     * @throws InvalidArgumentException
     * @throws RuntimeException
     * @throws \Exception
     */
    private function createNewWebDriver()
    {
        $this->log('创建会话');
        Cache::set('$sessionId', false);
        $driver = RemoteWebDriver::create($this->host, $this->config());
        $driver->manage()->window()->maximize();
        $sessionId = $driver->getSessionID();
        $this->log('$sessionId:' . $sessionId);
        Cache::set('$sessionId', $sessionId);
        $this->log('创建会话完成');
        return $driver;
    }

    /**
     * 刷新Driver
     * @throws InvalidArgumentException
     * @throws RuntimeException
     * @throws \Exception
     */
    protected function refreshDriver()
    {
        $this->log('刷新Driver');
        $this->clearDriver();

        $this->driver = $this->WebDriver();
        $this->log('刷新Driver完成');
    }

    /**
     * 检查Driver
     */
    protected function checkDriver()
    {
        if ($this->driver === null) {
            $this->driver = $this->WebDriver();
        }
    }
    /**
     * 关闭Driver
     */
    function clearDriver()
    {
        if ($this->driver !== null) {
            $this->driver->quit();
            $this->driver = null;
            Cache::set('$sessionId', false);
        }
    }

    function config()
    {
        $capabilities = DesiredCapabilities::chrome();
        $capabilities->setCapability(
            'goog:chromeOptions',
            [
                'prefs' => [
                    'download.default_directory' => '/home/seluser/Downloads',
                    'download.prompt_for_download' => false,
                    'download.directory_upgrade' => true,
                    'safebrowsing.enabled' => true,
                ],
            ]
        );
        return $capabilities;
    }

    public function status()
    {
        $sessions = $this->driver->getStatus();
        dump($sessions->isReady());
        dump($sessions->getMessage());
    }
    protected function setCookies()
    {
        if ($this->cookies  !== []) {
            $this->log('cookie登录');
            foreach ($this->cookies as $cookie) {
                $this->driver->manage()->addCookie($cookie);
            }
        }
    }

    function ask($tps = '请输入')
    {
        if (ob_get_level() > 0) {
            ob_flush();
        }
        fwrite(STDOUT, "$tps");
        return     trim(fgets(STDIN));
    }

    private function ocrCaptchaOnline($imgPath)
    {
        $this->log('验证码接口');
        $Http =   new Client();
        $res =    $Http->post('http://api.waizaowang.com/ai/captcha', [
            'headers' => [
                'Content-Type' => 'application/x-www-form-urlencoded',
            ],
            'form_params' => [
                'models' => 1003,
                'token' => '6554470371eba99bdaa27b265adf3fc4',
                'image' => base64_encode(file_get_contents($imgPath)),
            ],
        ])->getBody()->getContents();
        $this->log('验证码接口结果：' . $res);

        $res = json_decode($res, true);

        if ($res['code'] == 200 &&  $res['data'][0]['succsss']) {
            return $res['data'][0]['result'];
        } else {
            return false;
        }
    }
    protected function docker_restart()
    {
        $container_name = 'chrome';
        Cache::set('$sessionId', false);
        $this->log('重启容器');
        static::docer_containers_restart($container_name);
        $isRun = false;
        while ($isRun) {
            try {
                static::docer_check_run($container_name);
                $isRun = true;
            } catch (\Throwable $th) {
                //throw $th;
            }
            sleep(1);
        }
        sleep(5);
        $this->log('启动成功');
    }

    protected function docker_check($container_name = 'chrome')
    {
        $sleep = 0;
        $isRun = false;
        while ($isRun) {
            $isRun =  static::docer_check_run($container_name);
            if ($sleep > 2) {
                $this->log('需要重启');
                static::docer_containers_restart($container_name);
                sleep(5);
            } else {
                $this->log('无需重启');
            }
            $sleep++;
        }
    }
    protected function click_check_elem(WebDriverBy $select, $wait = 3)
    {
        $this->log('操作元素：' . $select->getValue());

        $Elemensts = $this->driver->findElements($select);
        if (!empty($Elemensts)) {
            $imgElement = $this->driver->findElement($select);
            $imgElement->click();
            $this->log('完成操作元素：' . $select->getValue());
            sleep($wait);
        } else {
            $this->log('操作失败' . $select->getValue());
        }
    }

    protected function getOptions()
    {
        return ['options' => [], 'info' => []];
    }


    function log($msg)
    {
        Log::info($msg);
        echo $msg . PHP_EOL;
    }
    function ocrApi($imgPath,   $apiUrl = 'http://127.0.0.1:5000/ocr')
    {
        // 检查文件是否存在
        if (!file_exists($imgPath)) {
            throw new InvalidArgumentException("File not found at path: {$imgPath}");
        }

        // 初始化cURL会话
        $ch = curl_init();

        // 设置cURL选项
        curl_setopt_array($ch, [
            CURLOPT_URL => $apiUrl,
            CURLOPT_POST => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POSTFIELDS => ['image' => new \CurlFile($imgPath)],
            CURLOPT_HTTPHEADER => ['Content-Type: multipart/form-data'],
        ]);

        // 执行cURL请求
        $response = curl_exec($ch);

        // 检查cURL错误
        if (curl_errno($ch)) {
            throw new RuntimeException('cURL error: ' . curl_error($ch));
        }

        // 获取HTTP响应状态码
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        // 关闭cURL会话
        curl_close($ch);

        // 根据HTTP响应状态码处理结果
        if ($httpCode !== 200) {
            throw new RuntimeException('Failed to upload image. Server responded with status code: ' . $httpCode);
        }

        // 返回响应内容
        return json_decode($response, true);
    }
    function open($do)
    {
        $this->driver->get($do['url']);
    }
    function command($do)
    {
        // shell_exec('php think  sosi:mgtstatus 3 1');

        $this->log('执行shell_exec命令');
        $res =   shell_exec($do['argument']);
        if ($res) {
            $this->log('执行shell_exec命令结果：' . $res);
        }
    }
    /**
     * 执行函数
     */
    function func($do)
    {
        $func =  $do['func'];
        if (function_exists($func)) {
            $this->log('执行函数：' . $do['func']);
            $func($this, $do);
        } else {
            $this->log('函数：' . $do['func'] . ' 不存在');
        }
    }

    /**
     * 执行类方法
     */
    function this($do)
    {
        $this->log('执行当前类方法：' . $do['this_method']);
        $method = $do['this_method'];
        $this->$method($this, $do);
    }

    function class($do)
    {
        call_user_func_array([$do['className'], $do['func']], $do['args']);
    }
    function window($do)
    {
        $option = $do['option'];
        $this->$option($do['value']);
    }
    function closeWindow()
    {
        // 获取所有窗口的句柄
        $windowHandles =  $this->driver->getWindowHandles();
        // 关闭新窗口
        $this->driver->switchTo()->window($windowHandles[count($windowHandles) - 1])->close();

        $this->log('关闭窗口');
        // 返回到原来的窗口
        $this->driver->switchTo()->window($windowHandles[0]);
    }
    function toWindow($windowId)
    {
        // 获取所有窗口句柄
        $windowHandles =  $this->driver->getWindowHandles();
        if (isset($windowHandles[$windowId])) {
            $this->driver->switchTo()->window($windowHandles[$windowId]);
            $this->log('窗口切换成功：' . $windowId);
        } else {
            $this->log('窗口不存在：' . $windowId);
        }
    }

    function getSelect($do)
    {
        $keys = array_keys($do);
        $by = null;
        if (in_array('id', $keys)) {
            $by = 'id';
        } else if (in_array('name', $keys)) {
            $by = 'name';
        } else if (in_array('className', $keys)) {
            $by = 'className';
        } else if (in_array('cssSelector', $keys)) {
            $by = 'cssSelector';
        } else if (in_array('linkText', $keys)) {
            $by = 'linkText';
        } else if (in_array('partialLinkText', $keys)) {
            $by = 'partialLinkText';
        } else if (in_array('tagName', $keys)) {
            $by = 'tagName';
        } else if (in_array('xpath', $keys)) {
            $by = 'xpath';
        }
        return     WebDriverBy::$by($do[$by]);;
    }

    function findElement($do)
    {
        if (is_string($do['option'])) {
            $this->options($do, $do['option']);
        } else {
            foreach ($do['option'] as $option => $value) {

                $this->options($do, $option, $value);
            }
        }
    }
    function options($do, $option, $value = null)
    {
        $select = $this->getSelect($do);

        $element = $this->driver->findElement($select);
        switch ($option) {

            case 'sendKeys':
                $this->log('sendKeys：' . $value);
                $element->sendKeys($value);
                break;

            case 'uploadFiles':

                if (is_string($option)) {
                    $value[] = $option;
                }

                if (is_array($value)) {
                    // 将文件路径设置为 input 元素的值
                    // 上传多张图片
                    foreach ($value as $imagePath) {
                        // $element->setFileDetector(new LocalFileDetector());
                        $this->log('sendKeys：' . $imagePath);

                        try {
                            $element->sendKeys($imagePath);
                        } catch (\Throwable $th) {
                            //throw $th;
                        }

                        sleep(1);
                    }
                }
                break;

            case 'select':
                // 使用 WebDriverSelect 类来操作下拉框
                $select = new WebDriverSelect($element);
                switch ($do['type']) {
                    case 'text':
                        $select->selectByVisibleText($do['value']);
                        break;
                    case 'value':
                        $select->selectByValue($do['value']);
                        break;
                    case 'index':
                        $select->selectByIndex($do['value']);
                        break;
                    default:
                        $select->selectByVisibleText($do['value']);
                }
                break;

            case 'click':

                $element->click();
                break;

            case 'doubleClick':

                // 创建一个Actions实例
                $actions = new WebDriverActions($this->driver);

                // 执行双击操作
                $actions->doubleClick($element)->perform();
                break;
            case 'Keys':
                $constant = constant('Facebook\WebDriver\WebDriverKeys::' . $value);
                if ($constant) {
                    $element->sendKeys($constant);
                } else {
                    $this->log('不存在的按键操作：Facebook\WebDriver\WebDriverKeys::' . $value);
                }
                break;
            case 'clear':
                $this->log('clear');
                $element->clear();
                break;
            case 'sleep':
                sleep($value);
                break;
            case 'script':
                $this->driver->executeScript($value, [$element]);
            default:
        }
    }


    function run_script($do)
    {
        $scripts = $do['option'];
        if (is_string($scripts)) {
            $scripts[] = $scripts;
        }
        if (is_array($scripts)) {
            foreach ($scripts as $key => $script) {
                $this->driver->executeScript($script);
            }
        }
    }

    function frame($do)
    {
        $option = $do['option'];
        switch ($option) {
            case 'inframe':
                $this->inframe($do);
                break;
            case 'outframe':
                $this->outFrame();
                break;
            case 'parentFrame':
                $this->parentFrame();
                break;
            default:
                $this->log('操作错误：');
        }
    }

    function inframe($do)
    {
        $select = $this->getSelect($do);
        $iframeElement = $this->driver->findElement($select);
        $this->driver->switchTo()->frame($iframeElement);
    }
    function outFrame()
    {
        $this->driver->switchTo()->defaultContent();
    }
    function parentFrame()
    {
        $this->driver->switchTo()->parentFrame();
    }

    function countChildElement($do)
    {
        $select = $this->getSelect($do);
        $parentElement = $this->driver->findElement($select);
        // 找到父元素下的所有子元素
        $childElements = $parentElement->findElements(WebDriverBy::tagName('*'));
        // 获取所有元素的个数
        return  count($childElements);
    }
    function checkNumberDo($do)
    {
        $this->log('开始执行:checkNumberDo');
        $func = $do['func'];
        $number = (int)$func($this->driver, $do);
        $this->log('获取元素数量:' . $number);
        if ($number) {

            $emptyArrays = array_fill(0, $number, []);
            $options = $do['setps'] ?: false;
            foreach ($emptyArrays as $null) {
                if (is_array($options)) {
                    foreach ($options as $option) {
                        try {
                            $this->runSetp($option);
                        } catch (\Throwable $th) {
                            //throw $th;
                        }
                    }
                }
            }
        }
    }

    function checkIsset($do)
    {
        // $select =  $this->getSelect($do);
        // $parentElements =  $this->driver->findElements($select);
        // $this->log('判断元素是否存在');
        // if (!empty($parentElements)) {
        //     $this->log('元素存在,开始执行对比程序');

        $func = $do['func'];

        if ((bool)$func($this->driver, $do)) {
            $this->log('对比成功');

            $options = isset($do['true']) ? $do['true'] : false;
        } else {
            $this->log('对比失败');

            $options = isset($do['false']) ? $do['false'] : false;
        }

        if (is_array($options)) {
            foreach ($options as $option) {
                $this->runSetp($option);
            }
        }
    }
}
